package com.client.koshercook.server;

import java.util.ArrayList;
import java.util.List;

import javax.jdo.annotations.Element;
import javax.jdo.annotations.EmbeddedOnly;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.gwt.user.client.rpc.IsSerializable;

/**Holds the required recipe information for Ingredient class.
 * The fields are required for filtering and sorting recipes for user.
@notice it contains copy of most functions of Recipes class*/
@PersistenceCapable
public class RecipeInfo implements IsSerializable{

	@PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key junk; //don't care about it. apps engine doesn't allow me to make this class embedded
	
	@Persistent
	private Key key; //the recipe key in data store
    
    @Persistent
    private String site; //the site the recipe is taken from
   
    @Persistent
    private int idInSite; //the id of the recipe in its site
    
    @Persistent
    private String name; // the name of the recipe

    @Persistent
    private short kosher; //dairy,meat or parve.
    
    @Persistent
    private String type; // the type of the recipe: cake,salad,...
    
    @Persistent
    private List<Key> ingredients; //set of ingredients in the recipe
 
    @Persistent
    private String uploader; // the name of the user who uploaded the recipe
    
    @Persistent
    private int cookTime = 0; //cook time is saved in minutes!
    
    @Persistent
    private double grade = 3; //the output grade will be between 1 and 5,only round numbers. initial value is 5
           
    @Persistent
    private int numOfGraders = 0; //how many people graded this recipe 
    
    @Persistent
    private int numOfFavorites = 0; //how many people added this recipe to their favorites

    @Persistent
    @Element(dependent = "true") //it should delete all ingWithAlters when the recipeInfo is deleted from DS
    private List<ingWithAlters> alters = new ArrayList<ingWithAlters>();
    //if there are alternative ingredients to an ingredient (in this particular recipe), it will go here
    
    /** constructor of RecipeInfo */
    public RecipeInfo(Recipes recipe) {
    	this.site = recipe.getSite();
    	this.idInSite = recipe.getIdInSite();
    	this.key = KeyFactory.createKey(Recipes.class.getSimpleName(), getLink());
        this.name = recipe.getRecipeName();
        this.kosher = recipe.getKosher();
        this.type = recipe.getType();
        this.ingredients = new ArrayList<Key>();
        ingredients.addAll(recipe.getIngredients());
        this.uploader = recipe.getUploader();
        this.cookTime = recipe.getCookTime();
        this.grade = recipe.getGrade();
        this.numOfGraders = recipe.getNumOfGraders();
        this.numOfFavorites = recipe.getNumOfFavorites();
        this.alters = new ArrayList<ingWithAlters>(); 
        for(ingWithAlters iwa : recipe.getAlters())
        	alters.add(new ingWithAlters(iwa));
    }
    
    /** constructor to get a copy of RecipeInfo */
    public RecipeInfo(RecipeInfo recipe) {
    	this.site = recipe.getSite();
    	this.idInSite = recipe.getIdInSite();
    	this.key = KeyFactory.createKey(Recipes.class.getSimpleName(), getLink());
        this.name = recipe.getRecipeName();
        this.kosher = recipe.getKosher();
        this.type = recipe.getType();
        this.ingredients = new ArrayList<Key>();
        ingredients.addAll(recipe.getIngredients());
        this.uploader = recipe.getUploader();
        this.cookTime = recipe.getCookTime();
        this.grade = recipe.getGrade();
        this.numOfGraders = recipe.getNumOfGraders();
        this.numOfFavorites = recipe.getNumOfFavorites();
        this.alters = new ArrayList<ingWithAlters>(); 
        for(ingWithAlters iwa : recipe.getAlters())
        	alters.add(new ingWithAlters(iwa));
    }

    public Key getObjectKey() {
    	return junk;
    }
    public Key getKey() {
        return key;
    }

    public String getSite() {
        return site;
    }
    
    public int getIdInSite() {
        return idInSite;
    }
    
    public String getLink() {
    	if(site.equals("http://www.kipa.co.il/food/matkon/")) return site+idInSite;
    	if(site.equals("http://www.chef-lavan.co.il/tabid/53/RecipeID/")) return site+idInSite+"/Default.aspx";
    	if(site.equals("matkonkasher")) return site+idInSite;
    	if(site.equals("http://www.rotev.co.il/last_video.aspx?recipe_id=")) return site+idInSite;
    	if(site.equals("http://www.stybel.co.il/recipe_full.php?id=")) return site+idInSite;
    	System.out.println("recipeInfo: ERROR! didn't find such site");
    	return ""; //get here = error
    }
    
    public void setRecipeName(String recipeName) {
        this.name = recipeName;
    }
    
    public String getRecipeName() {
        return name;
    }
    
    public void setKosher(short kosher) {
    	this.kosher = kosher;
    }
    
    public short getKosher() {
    	return kosher;
    }
    
    public String getKosherString() {
    	return KosherType.getString(kosher);
    }
            
    public void setType(String type) {
		this.type = type;
	}

	public String getType() {
		return type;
	}

	/** in minutes */
	public void setCookTime(int cookTime) {
        this.cookTime = cookTime;
    }
    
	/** in minutes */
    public int getCookTime() {
        return cookTime;
    }
    
    public boolean isCookTimeLessThen(int minutes) {
        return (cookTime<=minutes);
    }
    
    public void setIngredients(ArrayList<Key> ingredients) {
        this.ingredients = ingredients;
    }
    
    public void setIngredients(Key ingredients[]) {
    	this.ingredients.clear();
    	for(int i=0; i<ingredients.length; i++)
    		this.ingredients.add(ingredients[i]);
    }
    
    public int getGrade() {
        return (int) grade;
    }
    
    //new for RecipeInfo
    public void setGrade(int grade){
    	this.numOfGraders++;
    	this.grade = grade;
	}
    
    public int getNumOfGraders() {
    	return (int) numOfGraders;
    }
    
    public List<Key> getIngredients() {
        return ingredients;
    }
    
    public void addIngredient(Key ingredient) {
    	ingredients.add(ingredient);
    }
    
    public boolean hasIngredient(Key ingredient) {
    	for(Key ing : ingredients){
    		if(ing.equals(ingredient))
    			return true;
    	}
    	return false;
    }

	public void setUploader(String uploader) {
		this.uploader = uploader;
	}

	public String getUploader() {
		return uploader;
	}

	public List<ingWithAlters> getAlters() {
		return alters;
	}
	
	public void setAlters(List<ingWithAlters> alters) {
		this.alters = alters;
	}
	
	public boolean hasAnyAlters() {
		return alters.size()!=0;
	}
	
	/** adds an alternative alter to an ingredient original */
	public void addAlternative(Key original, Key alter){
		
		for(ingWithAlters sub : alters){ 					//checks if original already exists in alters
			if(sub.getBaseIng().equals(original)){	//if found original
				if(!sub.getAllAlters().contains(alter)) sub.addAlter(alter);						//add it to the set
				return;
			}
		}
		//if we are here, original does not exists yet
		alters.add(new ingWithAlters(original,alter));
	}
	
	/** checks if alter is an alternative to the ingredient original */
	public boolean isAlternative(Key original, Key alter){
		for(ingWithAlters sub : alters) {
			if(sub.getBaseIng().equals(original)){
				return sub.isAlter(alter);
			}
		}
		return false;
	}
		
	/** checks if one of alters is an alternative for original */
	public boolean isAlternative(String original, ArrayList<Key> alter){
		for(ingWithAlters sub : alters) {
			if(sub.getBaseIng().equals(original)){
				//found original
				for(Key s : alter)
					if(sub.isAlter(s))
						return true;	//found an alternative
				return false;	//didn't find an alternative
			}
		}
		//didn't found original
		return false;
	}
	
	/** checks if ingredient has an alternative (any alternative) */
	public boolean hasAlternativeFor(Key ingredient) {
		for(ingWithAlters sub : alters) {
			if(sub.getBaseIng().equals(ingredient)){
				return true;
			}
		}
		return false;
	}
	
	/** returns all the alternatives for ingredient */
	public List<Key> getAllAltersFor(Key ingredient) {
		for(ingWithAlters sub : alters)
			if(sub.getBaseIng().equals(ingredient)){
				return sub.getAllAlters();
			}
		return null;
	}
	
	/** checks if alter is an alternative for any(!) of the ingredients in this recipe */
	public boolean isAlterForOther(Key alter) {
		for(ingWithAlters sub : alters)
			if(sub.getAllAlters().contains(alter))
				return true;
		return false;
	}

	/**get a list with all ingredients and alters. the list might have duplicates*/
	public List<Key> getListOfIngredientsAndAlters() {
		List<Key> res = new ArrayList<Key>();
		res.addAll(ingredients);
		for(ingWithAlters iwa : alters) res.addAll(iwa.getAllAlters());
		return res;
	}
	
	/**set the numOfFavorites field*/
	public void setNumOfFavorites(int numOfFavorites) {
		this.numOfFavorites = numOfFavorites;
	}
	
	/**increase the numOfFavorites field by 1*/
	public void increaseNumOfFavorites() {
		this.numOfFavorites = numOfFavorites++;
	}
	
	/**decrease the numOfFavorites field by 1*/
	public void decreaseNumOfFavorites() {
		this.numOfFavorites = numOfFavorites--;
	}

	/**get how many people added this recipe to their favorites*/
	public int getNumOfFavorites() {
		return numOfFavorites;
	}
	
	/** printf some fields to make sure it was filled well.
	 * @when it's used just for debugging
	 */
	public void printSomeFacts() {
		System.out.print("RecipeInfo: recipe "+name);
		System.out.println(" with key "+key);
		System.out.println(". have "+ingredients.size()+" ings, including "+ingredients.get(0));
	}
	
	
	//multiple options for function suitFilters
	
	/** filter for kosher */
	public boolean suitFilters(short kosher) {
		return ((this.kosher==kosher || kosher==KosherType.ALL) && (!Debug.isBannedRecipe(this.getLink())));
	}
	
	/** filter for kosher and type*/
	public boolean suitFilters(short kosher, String type) {
		return (suitFilters(kosher) && (type.equals("הכל") || this.type.equals(type)));
	}
	
	/** filter for kosher and type sites and and maxCookTime*/
	public boolean suitFilters(short kosher, String type,List<String> forbiddenSites,int maxCookTime) {
		return (suitFilters(kosher,type) && this.isCookTimeLessThen(maxCookTime) && !forbiddenSites.contains(this.getSite()));
	}
	//TODO implement more options for suitFilters
}
